import datetime
import logging
import os
import urllib
from google.appengine.api import users
from google.appengine.api import urlfetch
from google.appengine.ext import db
from google.appengine.ext.db import djangoforms
from django import newforms as forms
from django.http import HttpResponse
from django.http import HttpResponseRedirect
from django.http import HttpResponseForbidden
from django.http import HttpResponseNotFound
import django.shortcuts
import django.template
from django.utils.translation import ugettext as _
from django.utils import simplejson
import models


if not django.template.libraries.get('fmap.library', None):
    django.template.add_to_builtins('fmap.library')


# http://fcase.com/
#FMAP_GMAP_API_KEY = 'ABQIAAAA39Rf6UeBvt0cfU8Y0sa5_RQEklNUjcUyNWNtwKDqkVo1a4eajxRjkVPqkbFpOm9njAIvLzSSsszS9Q'
# http://fmap.appspot.com/
#FMAP_GMAP_API_KEY = 'ABQIAAAA39Rf6UeBvt0cfU8Y0sa5_RSUGEa5E10IPdzCdJyGWyJK-fgSIxSASJZFLhdwzvf2S7iwMGX8uHpBVA'
# http://appspot.com/
FMAP_GMAP_API_KEY = 'ABQIAAAA39Rf6UeBvt0cfU8Y0sa5_RRlOb26qSyU154aZeLwOrF4C7-DphSk4amWOyoMx8w9rE6dXjVHEmygFQ'


# ##############################################################################
# Form Classes.
# ##############################################################################
class MPlaceForm(forms.Form):
    cname = forms.CharField(
        required=False,
        max_length=128,
        label=_('Chinese Name'),
        widget=forms.TextInput(attrs={'size': 32}))
    ename = forms.CharField(
        required=False,
        max_length=64,
        label=_('English Name'),
        widget=forms.TextInput(attrs={'size': 32}))
    street = forms.CharField(
        max_length=128,
        label=_('Street'),
        widget=forms.TextInput(attrs={'size': 32}))
    suite = forms.CharField(
        required=False,
        max_length=32,
        label=_('Suite'),
        widget=forms.TextInput(attrs={'size': 8}))
    city = forms.CharField(
        required=False,
        max_length=64,
        label=_('City'),
        widget=forms.TextInput(attrs={'size': 32}))
    state = forms.ChoiceField(
        required=False,
        label=_('State'),
        choices=models.STATE_CHOICES)
    zip = forms.CharField(
        required=False,
        max_length=32,
        label=_('Zip Code'),
        widget=forms.TextInput(attrs={'size': 32}))
    country = forms.ChoiceField(
        required=False,
        label=_('Country'),
        choices=models.COUNTRY_CHOICES)
    phone = forms.CharField(
        required=False,
        max_length=32,
        label=_('Phone'),
        widget=forms.TextInput(attrs={'size': 32}))
    fax = forms.CharField(
        required=False,
        max_length=32,
        label=_('Fax'),
        widget=forms.TextInput(attrs={'size': 32}))
    web = forms.CharField(
        required=False,
        max_length=512,
        label=_('Web'),
        widget=forms.TextInput(attrs={'size': 32}))


# ##############################################################################
# Decorators for request handlers.
# ##############################################################################
def login_required(func):
    def login_wrapper(request, *args, **kwargs):
        if not request.guser:
            logging.info("Anonymous user")
            return HttpResponseRedirect(users.create_login_url(request.path))

        logging.info("User: '%s'", request.guser)
        #user.lastseen = datetime.datetime.now()
        #user.put()

        return func(request, *args, **kwargs)
    return login_wrapper


def mp_required(func):
    def mp_wrapper(request, mpid, *args, **kwargs):
        mp = models.MapPlace.get_by_id(int(mpid))
        if mp is None:
            return HttpResponseNotFound(_('No pages exists with id: %(id)s.') % {'id': mpid})

        request.mp= mp
        return func(request, *args, **kwargs)
    return mp_wrapper


# ##############################################################################
# Request handlers.
# ##############################################################################
@mp_required
def showmp(request):
    return _respond(request, 'showmp.html', {'mp': request.mp})

@login_required
def newmp(request):
    # Normal GET request.
    if request.method != 'POST':
        form = MPlaceForm()
        return _respond(request, 'newmp.html', {'form': form})

    # POST request.
    form = MPlaceForm(request.POST)
    mp = _build_new_mp(request, form)
    if mp is None:
        return _respond(request, 'newmp.html', {'form': form})
    else:
        return HttpResponseRedirect('/showmp/%s' % mp.key().id())

def home(request):
    return _respond(request, 'main_map.html', {})

def mps(request):
    mps = db.GqlQuery("SELECT * FROM MapPlace")

    # Build an array of records.
    outa = []
    for mp in mps:
        # Each record is an array with fixed index for each field.
        mpa = [
            mp.key().id(),                       #  0: id
            _get_mp_title(mp.cname, mp.ename),   #  1: title
            mp.street,                           #  2: street
            _none2empty(mp.suite),               #  3: suite
            mp.city,                             #  4: city
            mp.state,                            #  5: state
            mp.zip,                              #  6: zip
            mp.country,                          #  7: country
            mp.lat,                              #  8: lat
            mp.lng,                              #  9: lng
            _none2empty(mp.phone)]               # 10: phone

        outa.append(mpa)

    output = simplejson.dumps(outa)
    return HttpResponse(output)


# ##############################################################################
# Internal functions.
# ##############################################################################
def _respond(request, template, params=None):
    if params is None:
        params = {}

    params['request']  = request
    if request.guser:
        params['user'] = request.user
        params['sign_out'] = users.create_logout_url(request.path)
    else:
        params['user'] = None
        params['sign_in']  = users.create_login_url(request.path)

    try:
        return django.shortcuts.render_to_response(template, params)
    except MemoryError:
        logging.exception('MemoryError')
        return django.http.HttpResponse('MemoryError')
    except AssertionError:
        logging.exception('AssertionError')
        return django.http.HttpResponse('AssertionError')

def _build_new_mp(request, form):
    if not form.is_valid():
        return None

    pdict = {}
    pdict['cname']    = form.cleaned_data['cname']
    pdict['ename']    = form.cleaned_data['ename']
    pdict['street']   = form.cleaned_data['street']
    pdict['suite']    = form.cleaned_data['suite']
    pdict['city']     = form.cleaned_data['city']
    pdict['state']    = form.cleaned_data['state']
    pdict['zip']      = form.cleaned_data['zip']
    pdict['country']  = form.cleaned_data['country']
    pdict['phone']    = form.cleaned_data['phone']
    pdict['fax']      = form.cleaned_data['fax']
    pdict['web']      = form.cleaned_data['web']

    # At least one of cname or ename should be provided.
    if not pdict['cname'] and not pdict['ename']:
        return None

    # At least a zip, or a city/state combination should be provided.
    if not pdict['zip'] and not pdict['city'] or not pdict['state']:
        return None

    # Call geocoder to get normalized address and geopt info. This pdict will
    # be updated after a successful call.
    retval = _get_geocoder(pdict)
    if not retval:
        return None

    pdict['createdby'] = request.user

    # Check and remove any empty values.
    rlist = []
    for k, v in pdict.iteritems():
        if not v:
            rlist.append(k)
    for k in rlist:
        del pdict[k]

    mp = models.MapPlace(**pdict)
    mp.put()

    return mp

def _get_geocoder(pdict):
    qstr = pdict['street']
    if pdict['city']:
        qstr = ''.join((qstr, ', ', pdict['city']))
    if pdict['state']:
        qstr = ''.join((qstr, ', ', pdict['state']))
    if pdict['zip']:
        qstr = ''.join((qstr, ', ', pdict['zip']))
    if pdict['country']:
        qstr = ''.join((qstr, ', ', pdict['country']))

    gurl = ''.join((
        'http://maps.google.com/maps/geo?output=json&key=', FMAP_GMAP_API_KEY,
        '&q=', qstr))
    eurl = urllib.quote(gurl, safe='/#%[]=:;$&()+,!?*')
    result = urlfetch.fetch(eurl)
    if result.status_code != 200:
        logging.info("Geocoder request: '%s' returned a non-200 http code: %d",
            eurl, result.status_code)
        return False
    else:
        logging.info("Geocoder req:'%s' resp:'%s'.", eurl, result.content)

    kml = simplejson.loads(result.content)
    if kml['Status']['code'] != 200:
        logging.info("Geocoder req: '%s' returned Status->code: %d", eurl,
            kml['Status']['code'])
        return False

    try:
        pmark = kml['Placemark'][0]

        pdict['lat'] = pmark['Point']['coordinates'][1]
        pdict['lng'] = pmark['Point']['coordinates'][0]

        cdict = pmark['AddressDetails']['Country']
        adict = cdict['AdministrativeArea']

        pdict['country'] = cdict['CountryNameCode']
        pdict['state'] = adict['AdministrativeAreaName']

        if adict.has_key('Locality'):
            ldict = adict['Locality']
        elif adict.has_key('SubAdministrativeArea'):
            ldict = adict['SubAdministrativeArea']['Locality']

        pdict['city'] = ldict['LocalityName']
        pdict['street'] = ldict['Thoroughfare']['ThoroughfareName']
        pdict['zip'] = ldict['PostalCode']['PostalCodeNumber']
    except KeyError, err:
        logging.error("Geocoder req:'%s', resp:'%s', KeyError exception:%s",
            eurl, result.content, err)
        return False

    return True

def _get_mp_title(cname, ename):
    if cname:
        if ename:
            title = '%s (%s)' % (cname, ename)
        else:
            title = cname
    else:
        title = ename

    return title

def _none2empty(str):
    if str is None:
        return ''
    else:
        return str
